`include "defines.v"
module cp0_reg(
	input wire clk,
	input wire rst,
	
	input wire writeCe_i, //是否写CP0中的寄存器
	input wire[4:0] wAddr_i, //要写的地址
	input wire[31:0] wData_i, //要写的数据
	
	input wire[5:0] int_i, //6个外部中断输入
	
	input wire[4:0] rAddr_i, //要读寄存器的地址
	//异常相关
	input wire[31:0] exception_type_i, //发生异常的指令的异常类型
	input wire[31:0] current_inst_addr_i, //发生异常的指令的地址
	input wire is_in_delaySlot_i, //发生异常的指令是否是延迟槽指令
	
	output reg[31:0] data_o, //读出的数据
	
	output reg[31:0] count_o, //Count寄存器的值
	output reg[31:0] compare_o, //Compare寄存器的值
	output reg[31:0] status_o, //Status寄存器的值
	output reg[31:0] cause_o, //Cause寄存器的值
	output reg[31:0] epc_o, //EPC寄存器的值
	output reg[31:0] prid_o, //PRId寄存器的值
	output reg[31:0] config_o, //Config寄存器的值
	output reg timer_int_o //定时中断发生
);
	//写操作
	always@(posedge clk)
		if(rst == `RstEnable)
		begin
			count_o <= `ZeroWord;
			compare_o <= `ZeroWord;
			status_o <= 32'h1000_0000;
			cause_o <= `ZeroWord;
			epc_o <= `ZeroWord;
			prid_o <= 32'h00_43_01_02 ; //制作者：C(ox43)
			config_o <= 32'h0000_8000;
			
			timer_int_o <= `InterruptNotAssert;
		end
		else
		begin
			count_o <= count_o + 1;
			cause_o[15:10] <= int_i;
			//定时时间到，产生定时器中断
			if(compare_o != `ZeroWord && count_o == compare_o)
				timer_int_o <= `InterruptAssert;
			if(writeCe_i == `WriteEnable)
				case(wAddr_i)
					`CP0_REG_COUNT:
						count_o <= wData_i;
					`CP0_REG_COMPARE:
					begin
						compare_o <= wData_i;
						timer_int_o <= `InterruptNotAssert;
					end
					`CP0_REG_STATUS:
						status_o <= wData_i;
					`CP0_REG_EPC:
						epc_o <= wData_i;
					`CP0_REG_CAUSE:
					begin
						cause_o[9:8] <= wData_i[9:8]; //IP[1:0]
						cause_o[23] <= wData_i[23]; //IV
						cause_o[22] <= wData_i[22]; //WP
					end
				endcase
			
			case(exception_type_i)
				32'h0000_0001: //发生中断
				begin
					status_o[1] <= 1'b1; //EXL置1
					cause_o[6:2] <= 5'b00000; //记录发生了中断，编码为0
					if(is_in_delaySlot_i == `InDelaySlot)
					begin
						epc_o <= current_inst_addr_i - 4;
						cause_o[31] <= 1'b1; //发生异常的指令是延迟槽指令，置1
					end
					else
					begin
						epc_o <= current_inst_addr_i;
						cause_o[31] <= 1'b0;
					end
				end
				
				32'h0000_0008: //系统调用
				begin
					status_o[1] <= 1'b1; //EXL置1
					cause_o[6:2] <= 5'b01000; //记录发生了系统调用，编码为8
					if(status_o[1] == 1'b0) 
						if(is_in_delaySlot_i == `InDelaySlot)
						begin
							epc_o <= current_inst_addr_i - 4;
							cause_o[31] <= 1'b1; //发生异常的指令是延迟槽指令，置1
						end
						else
						begin
							epc_o <= current_inst_addr_i;
							cause_o[31] <= 1'b0;
						end
				end
				32'h0000_000a: //无效指令
				begin
					status_o[1] <= 1'b1; //EXL置1
					cause_o[6:2] <= 5'b01010; //记录发生了无效指令异常，编码为10
					if(status_o[1] == 1'b0) 
						if(is_in_delaySlot_i == `InDelaySlot)
						begin
							epc_o <= current_inst_addr_i - 4;
							cause_o[31] <= 1'b1; //发生异常的指令是延迟槽指令，置1
						end
						else
						begin
							epc_o <= current_inst_addr_i;
							cause_o[31] <= 1'b0;
						end
				end
				32'h0000_000d: //自陷指令
				begin
					status_o[1] <= 1'b1; //EXL置1
					cause_o[6:2] <= 5'b01101; //记录发生了自陷指令，编码为13
					if(status_o[1] == 1'b0) 
						if(is_in_delaySlot_i == `InDelaySlot)
						begin
							epc_o <= current_inst_addr_i - 4;
							cause_o[31] <= 1'b1; //发生异常的指令是延迟槽指令，置1
						end
						else
						begin
							epc_o <= current_inst_addr_i;
							cause_o[31] <= 1'b0;
						end
				end
				32'h0000_000c: //溢出
				begin
					status_o[1] <= 1'b1; //EXL置1
					cause_o[6:2] <= 5'b01100; //记录发生了溢出异常，编码为12
					if(status_o[1] == 1'b0) 
						if(is_in_delaySlot_i == `InDelaySlot)
						begin
							epc_o <= current_inst_addr_i - 4;
							cause_o[31] <= 1'b1; //发生异常的指令是延迟槽指令，置1
						end
						else
						begin
							epc_o <= current_inst_addr_i;
							cause_o[31] <= 1'b0;
						end
				end
				32'h0000_000e: //异常返回
				begin
					status_o[1] <= 1'b0;
				end
			endcase
		end

	//读操作
	always@(*)
		if(rst == `RstEnable)
			data_o = `ZeroWord;
		else
			case(rAddr_i)
				`CP0_REG_COUNT:
					data_o = count_o;
				`CP0_REG_COMPARE:
					data_o = compare_o;
				`CP0_REG_STATUS:
					data_o = status_o;
				`CP0_REG_CAUSE:
					data_o = cause_o;
				`CP0_REG_EPC:
					data_o = epc_o;
				`CP0_REG_PRId:
					data_o = prid_o;
				`CP0_REG_CONFIG:
					data_o = config_o;
				default:
					data_o = `ZeroWord;
			endcase
endmodule